/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package locusts.server;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import locusts.common.World;
import locusts.common.entities.Entity;
import locusts.common.entities.EntityType;

/**
 *
 * @author hamish
 */
public class EventManager {

    private static final String TIMER_NAME = "EventTimer";
    private static final boolean IS_DAEMON = false;
    //
    private final Timer timer;
    //

    public EventManager() {
        timer = new Timer(TIMER_NAME, IS_DAEMON);
    }

    public void schedule(EventTask task) {

        if(task.getPeriod() == 0)
            timer.schedule(task, task.getDelay());
        else
            timer.schedule(task, task.getDelay(), task.getPeriod());
    }

    public void schedule(Collection<EventTask> tasks) {
        for (EventTask task : tasks)
            schedule(task);
    }

    public static abstract class EventTask extends TimerTask {

        public static final long NO_REPEAT = 0L;
        public static final long NO_DELAY = 0L;
        public static final long DEFAULT_DELAY = NO_DELAY;
        public static final long DEFAULT_PERIOD = NO_REPEAT;
        private final long delay;
        private final long period;

        public EventTask(long delay, long period) {
            this.delay = delay;
            this.period = period;
        }

        public EventTask(long delay) {
            this(delay, DEFAULT_PERIOD);
        }

        public EventTask() {
            this(DEFAULT_DELAY);
        }

        protected long getDelay() {
            return delay;
        }

        protected long getPeriod() {
            return period;
        }

        abstract public void run();
    }

    public static class SpawnEventTask extends EventTask {

        private final Game game;
        private final int size;
        private final int sizeVariance;
        private final EntityType type;
        private final Position pos;

        public SpawnEventTask(long delay, long period, Game game, int size,
                int sizeVariance, EntityType type, Position pos) {
            super(delay, period);

            this.game = game;
            this.size = size;
            this.sizeVariance = sizeVariance;
            this.type = type;
            this.pos = pos;
        }

        @Override
        public void run() {
            final List<Entity> ents = new ArrayList<Entity>();
            int n = size + (int)(Math.random() * sizeVariance * 2) - sizeVariance;
            for (int i = 0; i < n; i++)
                ents.add(GameFactory.makeEntity(game, type));
            pos.setPosition(ents);
            for (Entity e : ents)
                game.addEntity(e);
        }
    }

    public static interface Position {

        void setPosition(Entity e);

        void setPosition(List<Entity> e);
    }

    public static class AbsolutePosition implements Position {

        protected double x;
        protected double y;
        protected double o;

        public AbsolutePosition() {
            this(0, 0, 0);
        }

        public AbsolutePosition(double x, double y, double o) {
            this.x = x;
            this.y = y;
            this.o = o;
        }

        public void setPosition(Entity e) {
            e.setX(x);
            e.setY(y);
            e.setOrientation(o);
        }

        public void setPosition(List<Entity> ents) {
            for (Entity e : ents) {
                setPosition(e);
            }
        }
    }

    public static class VariablePosition extends AbsolutePosition {

        protected double vx;
        protected double vy;
        protected double vo;

        public VariablePosition() {
            this(0, 0, 0, 0, 0, 0);
        }

        public VariablePosition(double x, double y, double o,
                double vx, double vy, double vo) {
            super(x, y, o);
            this.vx = vx;
            this.vy = vy;
            this.vo = vo;
        }

        @Override
        public void setPosition(Entity e) {
            e.setX(x + rand() * vx);
            e.setY(y + rand() * vy);
            e.setOrientation(o + rand() * vo);
        }

        private double rand() {
            return (Math.random() * 2d) - 1d;
        }
    }

    public static class RandomPosition extends VariablePosition {

        public RandomPosition(World world) {
            super(world.getWidth() / 2, world.getHeight() / 2, Math.PI,
                    world.getWidth() / 2, world.getHeight() / 2, Math.PI);
        }
    }

    public static class ListedPosition implements Position {

        public static final int ORDER_ASCENDING = 1;
        public static final int ORDER_DESCENDING = 2;
        public static final int ORDER_RANDOM = 3;
        public static final int DEFAULT_ORDER = ORDER_ASCENDING;
        //
        private final List<Position> spawnPositions;
        private final int order;
        private int next;

        public ListedPosition() {
            this(DEFAULT_ORDER);
        }

        public ListedPosition(int order) {
            super();
            this.order = order;
            spawnPositions = new ArrayList<Position>();
            next = 0;
        }

        public void addSpawnPosition(Position pos) {
            spawnPositions.add(pos);
        }

        public void setPosition(Entity e) {
            if (spawnPositions.isEmpty())
                return;
            spawnPositions.get(next).setPosition(e);
            updateNext();
        }

        public void setPosition(List<Entity> e) {
            if (spawnPositions.isEmpty())
                return;
            spawnPositions.get(next).setPosition(e);
            updateNext();
        }

        private void updateNext() {
            switch (order) {
                case ORDER_ASCENDING:
                    next = (next + 1) % spawnPositions.size();
                    break;
                case ORDER_DESCENDING:
                    next = ((next == 0) ? spawnPositions.size() : next) - 1;
                    break;
                case ORDER_RANDOM:
                    next = (int) (Math.random() * spawnPositions.size());
                    break;
            }

        }
    }
}
